package com.asm.blog.common.exception;

import com.asm.blog.common.lang.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常处理
 * @RestControllerAdvice 注解的类 执行方法时是[无法页面跳转]的，等价于@ControllerAdvice+@ResponseBody，表示该方法返回json数据。
 *                       此注解 一般配合
 *
 * @ControllerAdvice 捕获异常后，[如果需要页面跳转就不能加]@ResponseBody，加了则该方法返回的是json数据。
 *                   是@Controller的一个增强版，可以实现三个方面的功能：全局异常处理、全局数据绑定、全局数据预处理
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {


    /**
     * 401 没有权限 异常
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(value = ShiroException.class) // 一般没有权限的都是 shiro异常的子类异常
    public Result handler(ShiroException e) {
        log.error("运行时异常：----------------{}", e);
        return Result.fail(401, e.getMessage(), null);
    }

    /**
     * 400 Bad Request 是由于明显的客户端错误（例如，格式错误的请求语法，太大的大小，无效的请求消息或欺骗性路由请求），服务器不能或不会处理该请求
     *
     * 在 ajax 请求后台数据时比较常见。产生 HTTP 400 错误的原因有：
     *
     * 1、前端提交数据的字段名称或者是字段类型和后台的实体类不一致，导致无法封装；
     * 2、前端提交的到后台的数据应该是 json 字符串类型，而前端没有将对象转化为字符串类型；
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result handler(MethodArgumentNotValidException e) {
        log.error("实体校验异常：----------------{}", e);
        BindingResult bindingResult = e.getBindingResult();
        ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get();

        return Result.fail(objectError.getDefaultMessage());
    }

    /**
     * 非法参数异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IllegalArgumentException.class)
    public Result handler(IllegalArgumentException e) {
        log.error("Assert异常：----------------{}", e);
        return Result.fail(e.getMessage());
    }

    /**
     * 运行时异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = RuntimeException.class)
    public Result handler(RuntimeException e) {
        log.error("运行时异常：----------------{}", e);
        return Result.fail(e.getMessage());
    }


    /**
     * token已失效异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = ExpiredCredentialsException.class)
    public Result handler(ExpiredCredentialsException e) {
        log.error("token已失效异常：----------------{}", e);
        return Result.fail(e.getMessage());
    }


    /**
     * 未经验证的异常【处理 未登录就登出异常】
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = UnauthenticatedException.class)
    public Result handler(UnauthenticatedException e) {
        log.error("未经验证的异常：----------------{}", e);
        return Result.fail("未经验证的异常，请先登录在操作：" + e.getMessage());
    }


    /**
     * 请求方法不支持 异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public Result handler(HttpRequestMethodNotSupportedException e) {
        log.error("请求方法不支持异常：----------------{}", e);
        return Result.fail(e.getMessage());
    }

}
